local base = _G
module ( "terminal" )

function create ( file_name, show_text, show_graph )
  if show_text == nil then
    show_text = true
  end
  if show_graph == nil then
    show_graph = true
  end
  
  font = base.Font ( )
  font:load_system ( "Arial", 14 )
  
  terminal = base.Sprite ( )
  terminal.canvas:set_font ( font, base.RED, 0.75 )
  base.display.viewport:add_child ( terminal )

  write = false
  text = show_text
  graph = show_graph

  traces = {}
  tracesi = {}
  msgs = {}
  fps = {}
  memory = {}
  maxMem = 0
  elapsed = 0

  if file_name then
    file = base.io.open ( file_name, "w+" )
  end

  timer = base.Timer ( )
  timer:start ( 16, true )
  timer.on_tick = function ( timer )
    update ( 16, timer:get_delta ( ) )
  end
end

function destroy ( )
  write = nil
  show = nil
  
  traces = nil
  tracesi = nil
  msgs = nil
  fps = nil
  memory = nil
  maxMem = nil
  elapsed = nil
  
  if file then
    file:close ( )
    file = nil
  end
  if terminal then
    base.display.viewport:remove_child ( terminal )
    terminal = nil
  end
  if timer then
    timer:stop ( )
    timer = nil
  end
end

-- logs a string message
function log ( sz, ... )
  sz = base.string.format ( sz, ... )
  base.table.insert ( msgs, sz )
  if file then
    file:write ( sz )
    file:flush ( )
  end
end

-- traces a string
function trace ( key, value )
  if value then
    if traces[key] == nil then
      base.table.insert ( tracesi, key )
    end
  else
    -- erase trace
    for i, v in base.ipairs ( tracesi ) do
      if v == key then
        base.table.remove ( tracesi, i )
      end
    end
  end
  traces[key] = value
end

function clear ( )
  traces = {}
  tracesi = {}
  msgs = {}
end

--
-- Update
--
function update ( interval, delta )
  local delta_ms = delta * interval
  elapsed = elapsed + delta_ms
  -- track fps
  local cfps = interval / delta_ms
  base.table.insert ( fps, 1 / delta )
  if #fps > 200 then
    base.table.remove ( fps, 1 )
  end
  local avg = 0
  local min = 60
  for i, v in base.ipairs ( fps ) do
    avg = avg + v
    min = base.math.min ( v, min )
  end
  avg = base.math.floor ( avg / #fps )
  min = base.math.floor ( min )
  -- track memory usage
  local kb = base.collectgarbage ( "count" )
  maxMem = base.math.max ( maxMem, kb )
  base.table.insert ( memory, kb )
  if #memory > 200 then
    base.table.remove ( memory, 1 )
  end
  
  -- update visual log 
  local c = terminal.canvas
  c:clear ( )
  
  local width = base.display.viewport.width
  local height = base.display.viewport.height
  local line = font:get_size ( )
  local maxLines = base.math.floor ( height / line ) - 2
  local x, y = -width / 2, height / 2 - line

  if text == true then
    -- traces
    for i, v in base.ipairs ( tracesi ) do
      c:move_to ( x, y )
      c:write ( v .. ":" .. traces[v] )
      y = y - line
      maxLines = maxLines - 1
    end

    -- show log messages
    local current = base.math.max ( #msgs - maxLines + 1, 1 )
    local e = base.math.min ( #msgs, maxLines )
    for i = 1, e do
      c:move_to ( x, y - i * line )
      c:write ( msgs[current] )
      current = current + 1
    end
  end

  if graph == true then
    -- graph
    local w, h = 200, 100
    local x2, y2 = width / 2 - w - 1, height / 2 - h - 1
    for i = 0, 10 do
      c:move_to ( x2, y2 + i / 10 * h )
      c:rel_line_to ( w, 0 )
    end
    c:move_to ( x2, y2 )
    c:rel_line_to ( 0, h )
    c:move_to ( x2 + w, y2 )
    c:rel_line_to ( 0, h )
    c:set_line_style ( 1, base.GRAY, 0.5 )
    c:stroke ( )
    c:move_to ( x2, y2 + fps[1] * h )
    -- fps
    for i, v in base.ipairs ( fps ) do
      c:line_to ( x2 + i / 200 * w, y2 + v * h )
    end
    c:set_line_style ( 1, base.RED, 1 )
    c:stroke ( )
    -- memory
    c:move_to ( x2, y2 + memory[1] / maxMem * h )
    for i, v in base.ipairs ( memory ) do
      c:line_to ( x2 + i / 200 * w, y2 + v / maxMem * h )
    end
    c:set_line_style ( 1, base.WHITE, 1 )
    c:stroke ( )
  end

  local e = elapsed / 1000
  local m = base.math.floor ( e / 60 )
  local s = base.math.floor ( e - ( m * 60 ) )
  local ms = base.math.floor ( ( e - ( m * 60 ) - s ) * 100 )
  local sz = base.string.format (  '%.2d:%.2d:%.2d', m, s, ms )
  trace ( "Time", sz )
  cfps = base.math.floor ( cfps * 60 )
  trace ( "FPS", cfps )
  kb = base.math.floor ( kb )
  trace ( "Memory", kb )
end
